package smallfrench.moteur.parser;

import java.util.ArrayList;
import java.util.HashMap;

import smallfrench.moteur.lexer.err.ErreurFinFichierInattendue;
import smallfrench.moteur.lexer.err.ErreurLexemeAttendu;
import smallfrench.moteur.lexer.err.ErreurLexemeInattendu;
import smallfrench.moteur.lexer.err.ErreurLexer;
import smallfrench.moteur.lexer.lexemes.Lexeme;
import smallfrench.moteur.lexer.lexemes.LexemeIdent;
import smallfrench.moteur.lexer.lexemes.TypeLexeme;
import smallfrench.moteur.noeuds.*;
import smallfrench.moteur.noeuds.bin.NEstun;
import smallfrench.moteur.noeuds.bin.NIndex;
import smallfrench.moteur.noeuds.bin.NOperateurBinaireMath;


/**
 * 
 * Le "parser", l’analyseur syntaxique du langage.<br>
 * <br>
 * Il est récursif descendant.<br>
 * <br>
 * @see LecteurLexemes
 * @author antoine1023
 */
public class Parser {

	private final LecteurLexemes lecteur;

	/**
	 * 
	 * @param lecteur Le lecteur contenant tous les lexème du script à analyser.
	 */
	private Parser(LecteurLexemes lecteur) {
		this.lecteur = lecteur;
	}

	/**
	 * Analyse une suite d’instructions.
	 * 
	 * <pre>
	 * programme : [expression]...
	 * </pre>
	 * 
	 * @param lecteur
	 * @return Liste des {@link Noeud}s.
	 * @throws ErreurLexer
	 */
	public static ArrayList<Noeud> parserProgramme(LecteurLexemes lecteur)
			throws ErreurLexer {

		Parser parser = new Parser(lecteur);

		ArrayList<Noeud> noeuds = new ArrayList<Noeud>();
		while (! lecteur.estFini()) {
			noeuds.add(parser.parserExpression());
		}
		return noeuds;
	}


	/**
	 * <pre>
	 * expression : instrComposee
	 * </pre>
	 * 
	 * @return {@link Noeud}
	 * @throws ErreurLexer
	 */
	private Noeud parserExpression() throws ErreurLexer {
		Noeud n = parserInstrComposee();
		lirePointVirguleOptionnel();
		return n;
	}

	/**
	 * <pre>
	 * instrComposee :
	 * 		instrSi
	 * 		| ("var" ident ["=" condition])
	 *	 	| ("tantque" blocParentheses) instrComposee
	 * 	 	| ("retour" [blocParentheses])
	 *		| "finboucle"
	 *	 	| ("essayer"  "siexception")
	 *		| assignation
	 * </pre>
	 *
	 * @return Le noeud créé
	 * @throws ErreurLexer
	 * 
	 */
	private Noeud parserInstrComposee() throws ErreurLexer {

		if (lecteur.estFini())
			throw new ErreurFinFichierInattendue();

		Lexeme tokenInstr = lecteur.suivant();
		switch (tokenInstr.type) {

		case SI:
			return parserSi(tokenInstr);

		case CLASSE:
			return parserClasse(tokenInstr);

		case VAR: {
			NIdentificateur noeudNom = parserIdentificateur();
			if (noeudNom == null)
				throw new ErreurLexemeAttendu(TypeLexeme.IDENTIFICATEUR,
						tokenInstr.fin);

			if (! lecteur.estFini()) {
				tokenInstr = lecteur.suivant();
				if (tokenInstr.type == TypeLexeme.EGAL_ASSIGN) {
					Noeud noeudDroite = parserCondition();
					return new NDeclaration(noeudNom.lexeme,
							noeudNom.nom, noeudDroite);
				}
				lecteur.precedent();
			}
			return new NDeclaration(noeudNom.lexeme, noeudNom.nom);
		}

		case TANTQUE: {
			NBloc noeudCondition = parserBlocParentheses();

			if (noeudCondition == null)
				throw new ErreurLexemeAttendu("condition", tokenInstr.fin);

			Noeud noeud = parserInstrComposee();
			return new NTantque(tokenInstr, noeudCondition, noeud);
		}

		case RETOUR: {

			/* L’ancienne syntaxe simple `retour` qui est censée retourner
			 * `rien` à été supprimée, parce qu’on écrivait souvent par
			 * erreur `retour x` en voulant retourner x en oubliant les
			 * parenthèses. Or, c’était `rien` qui était retourné dans ce cas.
			 * Cette erreur agacante n’était pas toujours très facile à
			 * trouver.
			 * 
			 * Maintenant, les parenthèses sont toujours requises, ce qui fait
			 * que `retour` s’utilise comme une simple fonction. On peut
			 * utiliser une syntaxe un peu simplifiée pour retourner
			 * `rien` en ne mettant rien entre les parenthèses : `retour()`
			 * est équivalent à `retour(rien)`. */

			NBloc bloc = parserBlocParentheses();
			if (bloc == null) {
				throw new ErreurLexemeAttendu(
						"parenthèse ouvrante", tokenInstr.fin);
			}
			return new NRetourFonction(tokenInstr, bloc);
		}

		case FINBOUCLE:
			return new NFinboucle(tokenInstr);

		case ESSAYER: {
			if (lecteur.estFini())
				throw new ErreurLexemeAttendu("instruction", tokenInstr.fin);

			// Le noeud susceptible de lancer une exception
			Noeud noeudEssai = parserInstrComposee();

			// Les noeuds des catch, des "siexception"
			ArrayList<NEssayer.NCatch> noeudsCatch =
					new ArrayList<NEssayer.NCatch>();

			while (! lecteur.estFini()) {

				// On vérifie qu’il y a bien un "siexception"
				Lexeme token = lecteur.suivant();
				if (token.type != TypeLexeme.SIEXCEPTION) {
					lecteur.precedent();
					break;
				}

				// On récupère l’identificateur de l’exception attrappée
				NIdentificateur ni = parserIdentificateur();
				if (ni == null)
					throw new ErreurLexemeAttendu(
							TypeLexeme.IDENTIFICATEUR, token.fin);

				// Puis la classe de l’exception à attrapper
				Noeud np = parserBlocParentheses();
				if (np == null)
					throw new ErreurLexemeAttendu(
							"classe d’exception", ni.lexeme.fin);

				if (lecteur.estFini())
					throw new ErreurLexemeAttendu(
							"instruction", np.lexeme.fin);

				Noeud nInstruction = parserInstrComposee();

				NEssayer.NCatch noeudCatch = new NEssayer.NCatch(
						np, ni.nom, nInstruction);

				noeudsCatch.add(noeudCatch);
			}

			// On vérifie qu’il y ai au moins un "siexception"
			if (noeudsCatch.size() == 0)
				throw new ErreurLexemeAttendu(
						TypeLexeme.SIEXCEPTION, tokenInstr.fin);

			return new NEssayer(tokenInstr, noeudEssai, noeudsCatch);
		}

		case POURCHAQUE: {

			// L’identificateur du "compteur"
			NIdentificateur ni = parserIdentificateur();
			if (ni == null) {
				throw new ErreurLexemeAttendu(
						TypeLexeme.IDENTIFICATEUR, tokenInstr.fin);
			}

			lire(TypeLexeme.DANS);

			Noeud iterable = parserBlocParentheses();
			if (iterable == null) {
				throw new ErreurLexemeAttendu("expression itérable",
						tokenInstr.fin);
			}

			Noeud nRepet = parserInstrComposee();

			return new NPourchaque(tokenInstr, ni.nom, iterable, nRepet);
		}

		default:
			break;
		}

		lecteur.precedent();
		return parserAssignation();
	}

	/**
	 * <pre>
	 * si : "si" blocParentheses instrComposee
	 *			["ousi" instrComposee]...
	 *			["sinon" instrComposee]
	 * </pre>
	 * @param lexemeSi Le lexème du mot-clé "classe"
	 * @return Le noeud
	 * @throws ErreurLexer
	 */
	private Noeud parserSi(Lexeme lexemeSi) throws ErreurLexer {

		// Les noeuds de chaques conditions
		ArrayList<Noeud> noeudsConditions = new ArrayList<Noeud>();

		// Les noeuds des instructions correspondantes aux conditions
		ArrayList<Noeud> noeudsVrais = new ArrayList<Noeud>();

		Noeud noeudCondition = parserBlocParentheses();
		if (noeudCondition == null)
			throw new ErreurLexemeAttendu(
					TypeLexeme.PARENTHESE_OUVRANTE, lexemeSi.fin);

		noeudsConditions.add(noeudCondition);

		Noeud noeudVrai = parserInstrComposee();
		noeudsVrais.add(noeudVrai);

		while (! lecteur.estFini()) {

			Lexeme token = lecteur.suivant();

			if (token.type == TypeLexeme.SINON) {

				Noeud noeudInstruction = parserInstrComposee();

				return new NSi(lexemeSi, noeudsConditions, noeudsVrais,
						noeudInstruction);

			} else if (token.type == TypeLexeme.OUSI) {

				noeudCondition = parserBlocParentheses();
				if (noeudCondition == null)
					throw new ErreurLexemeAttendu(
							TypeLexeme.PARENTHESE_OUVRANTE, lexemeSi.fin);
				noeudsConditions.add(noeudCondition);

				noeudVrai = parserInstrComposee();
				noeudsVrais.add(noeudVrai);

			} else {
				lecteur.precedent();
				break;
			}
		}

		return new NSi(lexemeSi, noeudsConditions, noeudsVrais, null);
	}

	/**
	 * <pre>
	 * classe : "classe" ident
	 *     ["etendant" "(" ident [ident]... ")"] (
	 *         [ident definitionFonction]...
	 *     )
	 * </pre>
	 * 
	 * @param tokenInstr Le lexème du mot-clé "classe"
	 * @return La classe
	 * @throws ErreurLexer
	 */
	private NClasse parserClasse(Lexeme tokenInstr)
			throws ErreurLexer {

		NIdentificateur noeudNom = parserIdentificateur();
		if (noeudNom == null)
			throw new ErreurLexemeAttendu("nom de classe", tokenInstr.fin);

		if (lecteur.estFini())
			throw new ErreurLexemeAttendu("corps de la classe",
					noeudNom.lexeme.fin);

		Noeud[] noeudsParentes = null;

		Lexeme tokenEtendant = lecteur.suivant();
		if (tokenEtendant.type == TypeLexeme.ETENDANT) {
			NBloc b = parserBlocParentheses();
			if (b == null)
				throw new ErreurLexemeAttendu("classes parentes",
						tokenInstr.fin);

			noeudsParentes = new Noeud[b.noeuds.size()];
			b.noeuds.toArray(noeudsParentes);
		} else {
			lecteur.precedent();
		}

		// On parse le corps de la classe qui est une suite d’entrées
		// nom-valeur.

		HashMap<String, NBlocFonction> fonctions =
				new HashMap<String, NBlocFonction>();

		NBlocFonction noeudCtor = null;
		String nom = null;

		Lexeme tpo = lecteur.suivant();
		if (tpo.type != TypeLexeme.PARENTHESE_OUVRANTE)
			throw new ErreurLexemeAttendu("corps de la classe",  tpo.debut);

		while (! lecteur.estFini()) {

			Lexeme t = lecteur.suivant();

			if (t.type == TypeLexeme.PARENTHESE_FERMANTE) {
				if (nom != null)
					// Un nom de méthode sans corps est présent à la fin
					throw new ErreurLexemeAttendu("corps de méthode",
							t.fin);

				return new NClasse(tokenInstr, noeudNom.nom,
						noeudsParentes,
						noeudCtor,
						fonctions);
			}

			if (nom == null) {

				if (t instanceof LexemeIdent)

					nom = ((LexemeIdent) t).ident;

				else if (t.type == TypeLexeme.CONSTRUCTEUR) {

					noeudCtor = parserDefinitionFonction();
					if (noeudCtor == null)
						throw new ErreurLexemeAttendu(
								"corps de constructeur", t.fin);

				} else
					throw new ErreurLexemeAttendu(
							"identificateur de méthode", t.fin);

			} else {

				lecteur.precedent();

				NBlocFonction noeudFn = parserDefinitionFonction();
				if (noeudFn == null)
					throw new ErreurLexemeAttendu(
							"corps de la méthode \"" + nom + "\"", t.fin);

				fonctions.put(nom, noeudFn);
				nom = null;
			}
		}
		throw new ErreurLexemeAttendu(TypeLexeme.PARENTHESE_FERMANTE, tpo.fin);
	}

	/**
	 * <code>assignation : operationBooleenne ["=" operationBooleenne]</code>
	 * 
	 * @return {@link Noeud}
	 * @throws ErreurLexer
	 */
	private Noeud parserAssignation() throws ErreurLexer {

		Noeud gauche = parserOperationBooleenne();
		if (! lecteur.estFini()) {
			Lexeme token = lecteur.suivant();
			if (token.type == TypeLexeme.EGAL_ASSIGN) {
				Noeud droite = parserOperationBooleenne();
				return new NAssign(token, gauche, droite);
			}
			lecteur.precedent();
		}
		return gauche;
	}


	/**
	 * <pre>
	 * operationBooleenne = condition [
	 * 			("et" | "ou" | "ouex") condition]...
	 * </pre>
	 * 
	 * @return Le noeud créé
	 * @throws ErreurLexer
	 */
	private Noeud parserOperationBooleenne() throws ErreurLexer {

		Noeud gauche = parserCondition();
		boucle:
			while (! lecteur.estFini()) {
				Lexeme t = lecteur.suivant();
				Noeud noeudDroit;

				switch (t.type) {

				case ET:
					noeudDroit = parserCondition();
					gauche = new NOperateurBinaireMath(gauche, t, noeudDroit);
					break;

				case OU:
					noeudDroit = parserCondition();
					gauche = new NOperateurBinaireMath(gauche, t, noeudDroit);
					break;

				case OUEX:
					noeudDroit = parserCondition();
					gauche = new NOperateurBinaireMath(gauche, t, noeudDroit);
					break;

				default:
					lecteur.precedent();
					break boucle;
				}
			}
		return gauche;
	}

	/** <pre>
	 * condition = somme [(
	 * 		"egal" | "inegal" |
	 * 		"inf" | "sup" |
	 * 		"infegal" | "supegal"
	 * 		| "estun"
	 * 		| "est" | ("est" "pas")
	 * 	) somme]...
	 * </pre>
	 * 
	 * @return Le noeud créé
	 * @throws ErreurLexer
	 */
	private Noeud parserCondition() throws ErreurLexer {

		Noeud gauche = parserSomme();
		boucle:
			while (! lecteur.estFini()) {
				Lexeme t = lecteur.suivant();
				switch (t.type) {
				case EGAL:
				case INEGAL:
				case SUP:
				case INF:
				case SUPEGAL:
				case INFEGAL: {
					Noeud droit = parserSomme();
					gauche = new NOperateurBinaireMath(gauche, t, droit);
					break;
				}

				case ESTUN: {
					Noeud droit = parserSomme();
					gauche = new NEstun(gauche, t, droit);
					break;
				}

				case EST: {
					if (! lecteur.estFini()) {
						Lexeme pas = lecteur.suivant();
						if (pas.type == TypeLexeme.PAS) {
							Noeud droit = parserSomme();
							gauche = new NOperateurBinaireMath(
									gauche, pas, droit);
							break;
						}
						lecteur.precedent();
					}

					Noeud droit = parserSomme();
					gauche = new NOperateurBinaireMath(gauche, t, droit);
					break;
				}

				default:
					lecteur.precedent();
					break boucle;
				}
			}
		return gauche;
	}

	/**
	 * <code>somme = terme [("+" | "-") terme]...</code>
	 * @return {@link Noeud}
	 * @throws ErreurLexer
	 */
	private Noeud parserSomme() throws ErreurLexer {

		Noeud gauche = parserTerme();
		while (! lecteur.estFini()) {
			Lexeme t = lecteur.suivant();
			if (t.type == TypeLexeme.PLUS || t.type == TypeLexeme.MOINS) {
				Noeud droit = parserTerme();
				gauche = new NOperateurBinaireMath(gauche, t, droit);
			} else {
				lecteur.precedent();
				break;
			}
		}
		return gauche;
	}

	/**
	 * Analyse un terme.
	 * <code>terme = unaire [("*" | "/" | "modulo") unaire]...</code>
	 *
	 * @return {@link Noeud}
	 * @throws ErreurLexer
	 */
	private Noeud parserTerme() throws ErreurLexer {

		Noeud gauche = parserUnaire();

		while (! lecteur.estFini()) {

			Lexeme t = lecteur.suivant();

			if (t.type == TypeLexeme.ETOILE
					|| t.type == TypeLexeme.SLASH
					|| t.type == TypeLexeme.MODULO) {

				Noeud droit = parserUnaire();
				gauche = new NOperateurBinaireMath(gauche, t, droit);

			} else {
				lecteur.precedent();
				break;
			}
		}
		return gauche;
	}

	/**
	 * Parse un opérateur unaire.<br><br>
	 * <code>unaire : ["-" | "non"] membreAppelIndex</code>
	 * 
	 * @return Le {@link Noeud}
	 * @throws ErreurLexer
	 */
	private Noeud parserUnaire() throws ErreurLexer {

		if (lecteur.estFini())
			throw new ErreurFinFichierInattendue();

		Lexeme t = lecteur.suivant();
		Noeud e;

		switch (t.type) {
		case MOINS:
			e = parserMembreAppelIndex();
			return new NMoinsUnaire(t, e);

		case NON:
			e = parserMembreAppelIndex();
			return new NNonUnaire(t, e);

		case CLASSEDE:
			e = parserMembreAppelIndex();
			return new NClasseDe(t, e);

		default:
			break;
		}
		lecteur.precedent();
		return parserMembreAppelIndex();
	}

	/**
	 * Opérateurs ".", "..", appel de fonction et accès à un élément
	 * d’un tableau.
	 * <br>
	 * 
	 * <pre>
	 * membreAppelIndex :
	 * 		basic [("." nom) | blocParentheses | elementTableau]...
	 * </pre>
	 *
	 * @return {@link Noeud}
	 * @throws ErreurLexer
	 */
	private Noeud parserMembreAppelIndex() throws ErreurLexer {

		Noeud gauche = parserBasic();

		boucle:
			while (! lecteur.estFini()) {
				Lexeme t = lecteur.suivant();
				switch (t.type) {

				case POINT: {
					NIdentificateur nom = parserIdentificateur();
					if (nom == null) {
						throw new ErreurLexemeAttendu(
								TypeLexeme.IDENTIFICATEUR, t.fin);
					}
					gauche = new NPoint(t, gauche, nom.nom);
					break;
				}

				case POINT_POINT: {
					NIdentificateur nNom = parserIdentificateur();
					String nom;
					if (nNom == null) {
						Lexeme l = lecteur.suivant();
						if (l.type == TypeLexeme.CONSTRUCTEUR) {
							nom = "constructeur";
						} else {
							throw new ErreurLexemeAttendu(
									TypeLexeme.IDENTIFICATEUR, t.fin);
						}
					} else {
						nom = nNom.nom;
					}
					gauche = new NPointPoint(t, gauche, nom);
					break;
				}

				case PARENTHESE_OUVRANTE:
					lecteur.precedent();
					NBloc params = parserBlocParentheses();
					gauche = new NAppelFonction(t, gauche, params.noeuds);
					break;

				case CROCHET_OUVRANT:
					lecteur.precedent();
					NBloc index = parserElementTableau();
					gauche = new NIndex(gauche, t, index);
					break;

				default:
					lecteur.precedent();
					break boucle;
				}
			}
		return gauche;
	}

	/**
	 * basic : definitionFonction | blocParentheses
	 * 			| identificateur | constante
	 *
	 * @return NExpr ou null
	 * @throws ErreurLexer
	 */
	private Noeud parserBasic() throws ErreurLexer {

		Noeud e;
		e = parserDefinitionFonction();
		if (e != null)
			return e;

		e = parserBlocParentheses();
		if (e != null)
			return e;

		e = parserConstante();
		if (e != null)
			return e;

		e = parserIdentificateur();
		if (e != null)
			return e;

		throw new ErreurLexemeInattendu(lecteur.suivant());
	}
	/**
	 * terminal
	 * 
	 * @return NVar ou null, si pas d’identificateur.
	 */
	private NIdentificateur parserIdentificateur() {
		if (lecteur.estFini())
			return null;
		Lexeme t = lecteur.suivant();
		if (t instanceof LexemeIdent) {
			NIdentificateur r = new NIdentificateur(t,
					((LexemeIdent) t).ident);
			return r;
		}
		lecteur.precedent();
		return null;
	}


	/**
	 * constante : nombre | chaine | "moi" | "vrai" | "faux" | "rien"
	 * 
	 * @return NConstante ou <code>null</code>
	 */
	private Noeud parserConstante() {

		if (lecteur.estFini()) return null;

		Lexeme l = lecteur.suivant();

		switch (l.type) {

		case CONST_ENTIER:
		case CONST_CHAINE:
		case CONST_FLOTTANT:
		case CONST_VRAI:
		case CONST_FAUX:
		case CONST_RIEN:
			return new NConstante(l);

		case MOI:
			return new NMoi(l);

		default:
			break;
		}

		lecteur.precedent();
		return null;
	}

	/**
	 * Lis un lexème.
	 * 
	 * @param type Le type du lexème attendu
	 * @return Le lexème lu
	 * @throws ErreurLexer Si fin de fichier ou si le lexème lu ne correspond
	 * pas au type attendu
	 */
	private Lexeme lire(TypeLexeme type) throws ErreurLexer {

		if (lecteur.estFini()) {
			throw new ErreurLexemeAttendu(
					type.toString(), lecteur.getPosition());
		}

		Lexeme l = lecteur.suivant();

		if (l.type != type) {
			throw new ErreurLexemeAttendu(type, lecteur.getPosition());
		}

		return l;
	}

	/**
	 * Lis un éventuel point virgule.
	 * 
	 * @return Le lexème du point virgule lu ; ou <code>null</code> si pas de
	 * point virgule rencontré
	 */
	private Lexeme lirePointVirguleOptionnel() {
		if (lecteur.estFini())
			return null;
		Lexeme l = lecteur.suivant();
		if (l.type == TypeLexeme.POINT_VIRGULE)
			return l;
		lecteur.precedent();
		return null;
	}

	/**
	 * Bloc de parenthèses.
	 * blocParentheses : "(" [expr]... ")"...
	 * 
	 * @return NBloc ou <code>null</code>
	 * @throws ErreurLexer
	 */
	private NBloc parserBlocParentheses() throws ErreurLexer {

		if (lecteur.estFini()) return null;

		Lexeme t = lecteur.suivant();
		if (t.type == TypeLexeme.PARENTHESE_OUVRANTE) {
			ArrayList<Noeud> children = new ArrayList<Noeud>();
			Lexeme pf;
			while (! lecteur.estFini()) {
				pf = lecteur.suivant();
				if (pf.type == TypeLexeme.PARENTHESE_FERMANTE) {
					return new NBloc(t, children);
				}
				lecteur.precedent();
				children.add(parserExpression());
			}
			throw new ErreurLexemeAttendu(
					TypeLexeme.PARENTHESE_FERMANTE, t.fin);
		}
		lecteur.precedent();
		return null;
	}

	/**
	 * Bloc de crochets, accès à un élément de tableau.<br>
	 * 
	 * <pre>
	 * definitionTableau : "[" [expr]... "]"...
	 * </pre>
	 * 
	 * @return NBloc ou <code>null</code>
	 * @throws ErreurLexer
	 */
	private NBloc parserElementTableau() throws ErreurLexer {

		if (lecteur.estFini()) return null;

		Lexeme t = lecteur.suivant();
		if (t.type == TypeLexeme.CROCHET_OUVRANT) {
			ArrayList<Noeud> children = new ArrayList<Noeud>();
			Lexeme pf;
			while (! lecteur.estFini()) {
				pf = lecteur.suivant();
				if (pf.type == TypeLexeme.CROCHET_FERMANT) {
					return new NBloc(t, children);
				}
				lecteur.precedent();
				children.add(parserExpression());
			}
			throw new ErreurLexemeAttendu(TypeLexeme.CROCHET_FERMANT, t.fin);
		}
		lecteur.precedent();
		return null;
	}

	/**
	 * <pre>
	 * definitionFonction = "{" [expression [expression]... ":"]
	 * 			[expression]... "}"...
	 * </pre>
	 * 
	 * @return NBlocFonction ou <code>null</code>
	 * @throws ErreurLexer
	 */
	private NBlocFonction parserDefinitionFonction()
			throws ErreurLexer {

		if (lecteur.estFini()) return null;

		Lexeme t = lecteur.suivant();
		if (t.type == TypeLexeme.ACCOLADE_OUVRANTE) {

			ArrayList<Noeud> noeudsEnfants = new ArrayList<Noeud>();
			int nombreParametres = 0;

			while (! lecteur.estFini()) {

				t = lecteur.suivant();

				if (t.type == TypeLexeme.ACCOLADE_FERMANTE) {
					return creerDefinitionFonction(t, noeudsEnfants,
							nombreParametres);
				} else if (t.type == TypeLexeme.DEUX_POINTS) {
					nombreParametres = noeudsEnfants.size();
					continue;
				}

				lecteur.precedent();
				noeudsEnfants.add(parserExpression());
			}
			throw new ErreurLexemeAttendu(TypeLexeme.ACCOLADE_FERMANTE, t.fin);
		}
		lecteur.precedent();
		return null;
	}

	/**
	 * Vérifie la cohérence du bloc de définition de fonction,  puis créé le
	 * noeud {@link NBlocFonction}
	 * 
	 * @param token
	 * @param noeuds La liste des noeuds de la fonction, avec les arguments
	 * et le ":"
	 * @param nombreParametres Le nombre de paramètres (de noeuds avant le ":")
	 * @return {@link NBlocFonction}, la définition de la fonction
	 * @throws ErreurLexer
	 */
	private static NBlocFonction creerDefinitionFonction(Lexeme token,
			ArrayList<Noeud> noeuds, int nombreParametres)
					throws ErreurLexer {

		ArrayList<String> nomsParams = new ArrayList<String>();

		for (int i = 0; i < nombreParametres; i++) {
			Noeud param = noeuds.remove(0);
			if (! (param instanceof NIdentificateur)) {
				throw new ErreurLexemeInattendu(param.lexeme);
			}
			nomsParams.add(((NIdentificateur)param).nom);
		}

		return new NBlocFonction(token, nomsParams, noeuds);
	}

}
